package nameServerPersistent;

import generalObjects.BaseClientContract;
import generalObjects.CassandraHandler;

import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.ServerNotActiveException;
import java.rmi.server.UnicastRemoteObject;

import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.ResultSet;

/**
 * Constitutes the GCom Nameserver, the server uses Java RMI to provide methods
 * for joining groups by providing a groupname. If the group exists the leader
 * of that group is returned. If the group does not exist the group is created,
 * registered in the nameserver and the calling client becomes the
 * leader/creator of that group.
 * 
 */
public class Server implements ServerRMI {

    private final String readLeaderQuery = "SELECT leader FROM groupleaders WHERE groupname=?";
    private final String insertLeaderQuery = "INSERT INTO groupleaders (leader,groupname) VALUES (?,?)";
    private final String replaceDeadLeaderQuery = "UPDATE groupleaders SET leader=? WHERE groupname=?";

    private final CassandraHandler casHand;
    private final String[] cassandraNodes;

    public Server(final String[] args) throws RemoteException {
        super();
        cassandraNodes = args;
        // Create the cassandra connection handler.
        casHand = new CassandraHandler(cassandraNodes, "GCom");

        final ServerRMI stub = (ServerRMI) UnicastRemoteObject.exportObject(
                this, 0);
        final Registry registry = LocateRegistry.createRegistry(1099);
        registry.rebind("Nameserver", stub);
        System.out.println("Server online on port 1099");
    }

    @Override
    public Remote join(final String gName, final Remote caller)
            throws RemoteException, ServerNotActiveException {

        // Execute the READ statement
        final ResultSet resultSet = casHand.executeStatement(readLeaderQuery,
                ConsistencyLevel.QUORUM, gName);

        if (resultSet.isExhausted()) {
            // no leader exists, calling client becomes leader of group.
            casHand.executeStatement(insertLeaderQuery,
                    ConsistencyLevel.QUORUM, casHand.serializeObject(caller),
                    gName);
            return null;
        }
        // There was a registered leader, check that he's alive.
        final BaseClientContract leader = (BaseClientContract) casHand
                .deSerializeObject(resultSet.one().getBytes("leader"));
        try {
            leader.isAlive();
        } catch (final RemoteException e) {
            // unable to contact leader. The caller becomes new leader.
            casHand.executeStatement(replaceDeadLeaderQuery,
                    ConsistencyLevel.QUORUM, casHand.serializeObject(caller),
                    gName);
            return null;
        }
        return leader;
    }

    @Override
    public boolean rebindLeader(final String gName, final Remote newleader)
            throws RemoteException, ServerNotActiveException {

        // Execute the UPDATE statement
        casHand.executeStatement(replaceDeadLeaderQuery,
                ConsistencyLevel.QUORUM, casHand.serializeObject(newleader),
                gName);
        // TODO always returns true now that we can't easily determine if a
        // replace was successful.
        return true;

    }

    @Override
    public String[] getCassandraNodes() throws RemoteException,
            ServerNotActiveException {
        return cassandraNodes;
    }

}